package com.playfair.core;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

import com.playfair.actions.ActionManager;
import com.playfair.components.Digraph;
import com.playfair.components.Matrix;
import com.playfair.thread.BruteForceAttackerTrigrams;
import com.playfair.thread.utils.BruteForceAttackComparator;
import com.playfair.thread.utils.BruteForceAttackResult;

public class AppCore {

	private static AppCore instance;
	private ActionManager actionManager;
	private String file = "src\\com\\playfair\\base-dictionary\\thread\\",
			codeToBreak, bestPlainText = null;
	private ArrayList<BruteForceAttackResult> possibleSolutions;
	private Character finalChar, interChar, interchangeChar1, interchangeChar2;
	private HashMap<String, Double> trigrams;

	private AppCore() {
		actionManager = new ActionManager();
		setInterChar(new Character('X'));
		setFinalChar(new Character('Z'));
		setInterchangeChar1(new Character('J'));
		setInterchangeChar2(new Character('I'));
	}

	public static AppCore getInstance() {
		if (instance == null) {
			instance = new AppCore();
		}
		return instance;
	}

	public ArrayList<BruteForceAttackResult> owenAttackTrigram(String code,
			Integer nmbOfThreads) {
		trigrams = new HashMap<String, Double>();
		String line;
		FileInputStream fis = null;
		BufferedReader br = null;

		try {
			fis = new FileInputStream(file + "trigrams");
			br = new BufferedReader(new InputStreamReader(fis,
					Charset.forName("UTF-8")));

			while ((line = br.readLine()) != null) {
				String tmp = br.readLine();
				Double value = Double.valueOf(tmp);
				trigrams.put(line, value);
			}
			br.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		possibleSolutions = new ArrayList<BruteForceAttackResult>();
		codeToBreak = code;

		if (nmbOfThreads == 3) {
			Thread thread1, thread2, thread3;

			thread1 = new Thread(new BruteForceAttackerTrigrams(file, 3, 1));
			thread2 = new Thread(new BruteForceAttackerTrigrams(file, 3, 2));
			thread3 = new Thread(new BruteForceAttackerTrigrams(file, 3, 3));

			thread1.start();
			thread2.start();
			thread3.start();
			while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive())
				;
		} else if (nmbOfThreads == 4) {
			Thread thread1, thread2, thread3, thread4;

			thread1 = new Thread(new BruteForceAttackerTrigrams(file, 4, 1));
			thread2 = new Thread(new BruteForceAttackerTrigrams(file, 4, 2));
			thread3 = new Thread(new BruteForceAttackerTrigrams(file, 4, 3));
			thread4 = new Thread(new BruteForceAttackerTrigrams(file, 4, 4));

			thread1.start();
			thread2.start();
			thread3.start();
			thread4.start();
			while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive()
					|| thread4.isAlive())
				;
		} else if (nmbOfThreads == 5) {
			Thread thread1, thread2, thread3, thread4, thread5;

			thread1 = new Thread(new BruteForceAttackerTrigrams(file, 5, 1));
			thread2 = new Thread(new BruteForceAttackerTrigrams(file, 5, 2));
			thread3 = new Thread(new BruteForceAttackerTrigrams(file, 5, 3));
			thread4 = new Thread(new BruteForceAttackerTrigrams(file, 5, 4));
			thread5 = new Thread(new BruteForceAttackerTrigrams(file, 5, 5));

			thread1.start();
			thread2.start();
			thread3.start();
			thread4.start();
			thread5.start();
			while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive()
					|| thread4.isAlive() || thread5.isAlive())
				;
		}

		Collections.sort(possibleSolutions, new BruteForceAttackComparator());
		// return possibleSolutions;
		if (possibleSolutions.size() > 11)
			possibleSolutions = new ArrayList<BruteForceAttackResult>(
					possibleSolutions.subList(0, 11));
		return possibleSolutions;
	}

	public ArrayList<BruteForceAttackResult> owenAttack(String code,
			Integer nmbOfThreads) {
		// possibleSolutions = new ArrayList<BruteForceAttackResult>();
		// codeToBreak = code;
		// if (nmbOfThreads == 3) {
		// Thread thread1, thread2, thread3;
		//
		// thread1 = new Thread(new BruteForceAttacker(file, 3, 1));
		// thread2 = new Thread(new BruteForceAttacker(file, 3, 2));
		// thread3 = new Thread(new BruteForceAttacker(file, 3, 3));
		//
		// thread1.start();
		// thread2.start();
		// thread3.start();
		// while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive())
		// ;
		// } else if (nmbOfThreads == 4) {
		// Thread thread1, thread2, thread3, thread4;
		//
		// thread1 = new Thread(new BruteForceAttacker(file, 4, 1));
		// thread2 = new Thread(new BruteForceAttacker(file, 4, 2));
		// thread3 = new Thread(new BruteForceAttacker(file, 4, 3));
		// thread4 = new Thread(new BruteForceAttacker(file, 4, 4));
		//
		// thread1.start();
		// thread2.start();
		// thread3.start();
		// thread4.start();
		// while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive()
		// || thread4.isAlive())
		// ;
		// } else if (nmbOfThreads == 5) {
		// Thread thread1, thread2, thread3, thread4, thread5;
		//
		// thread1 = new Thread(new BruteForceAttacker(file, 5, 1));
		// thread2 = new Thread(new BruteForceAttacker(file, 5, 2));
		// thread3 = new Thread(new BruteForceAttacker(file, 5, 3));
		// thread4 = new Thread(new BruteForceAttacker(file, 5, 4));
		// thread5 = new Thread(new BruteForceAttacker(file, 5, 5));
		//
		// thread1.start();
		// thread2.start();
		// thread3.start();
		// thread4.start();
		// thread5.start();
		// while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive()
		// || thread4.isAlive() || thread5.isAlive())
		// ;
		// }
		// Collections.sort(possibleSolutions, new
		// BruteForceAttackComparator());
		//
		// if (possibleSolutions.size() > 11)
		// possibleSolutions = new ArrayList<BruteForceAttackResult>(
		// possibleSolutions.subList(0, 11));

		return possibleSolutions;

	}

	// @SuppressWarnings("resource")
	// public String hillClimbAttack(String code) {
	// Matrix currentMatrix, bestMatrix;
	// String bestPlainText = "NEMA", line;
	// Double currentTemp = new Double(0.0), bestTemp = new Double(0.0);
	// Digraph digraph = new Digraph(code);
	// FileInputStream fis = null;
	// BufferedReader br = null;
	//
	// int x = 0;
	// while (x++ < 500000) {
	// if (x % 1000 == 0)
	// System.out.println(x);
	// currentTemp = 0.0;
	// currentMatrix = new Matrix();
	// String plainText = decrypt(currentMatrix, digraph);
	// try {
	// fis = new FileInputStream("baza2 - Copy.TXT");
	// br = new BufferedReader(new InputStreamReader(fis,
	// Charset.forName("UTF-8")));
	// while ((line = br.readLine()) != null) {
	// line = line.trim();
	// if (plainText.contains(line.trim())) {
	// currentTemp = (double) line.length()
	// / (double) plainText.length();
	// }
	// }
	// if (currentTemp > bestTemp) {
	// bestTemp = currentTemp;
	// bestPlainText = plainText;
	// bestMatrix = currentMatrix;
	// System.out.println("FOUND: " + bestPlainText);
	// }
	//
	// } catch (FileNotFoundException e) {
	// e.printStackTrace();
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	//
	// }
	// return bestPlainText;
	// }

	public String decrypt(Matrix matrix, Digraph digraph) {
		HashMap<Character, Integer[]> mainMatrix = matrix.getMainMatrix();
		Character[][] playFairMatrix = matrix.getPlayFairMatrix();
		ArrayList<Character[]> digraphList = digraph.getDigraph(), encriptedDigraph = new ArrayList<Character[]>();
		for (Character[] digraphPair : digraphList) {
			Integer[] first = mainMatrix.get(digraphPair[0]), second = matrix
					.getMainMatrix().get(digraphPair[1]);
			Character[] pair = new Character[2];
			// is rectangle
			if (first[0] != second[0] && first[1] != second[1]) {
				pair[0] = playFairMatrix[first[0]][second[1]];
				pair[1] = playFairMatrix[second[0]][first[1]];
			}
			// same row
			else if (first[1] == second[1]) {
				int n = (first[0] - 1) % 5;
				if (n < 0)
					n = 5 + n;
				pair[0] = playFairMatrix[n][first[1]];
				n = (second[0] - 1) % 5;
				if (n < 0)
					n = 5 + n;
				pair[1] = playFairMatrix[n][second[1]];
			}
			// same column
			else {
				int n = (first[1] - 1) % 5;
				if (n < 0)
					n = 5 + n;
				pair[0] = playFairMatrix[first[0]][n];
				n = (second[1] - 1) % 5;
				if (n < 0)
					n = 5 + n;
				pair[1] = playFairMatrix[second[0]][n];
			}
			encriptedDigraph.add(pair);
		}
		String ret = "";
		for (Character[] cha : encriptedDigraph) {
			ret += cha[0].toString();
			ret += cha[1].toString();
		}
		return ret;
	}

	public String decrypt(String key, String code) {
		Matrix matrix = new Matrix(key);
		Digraph digraph = new Digraph(code);
		// matrix.printMatrix();
		// digraph.printDigraph();
		return decrypt(matrix, digraph);
	}

	public String encrypt(String key, String text) {
		Matrix matrix = new Matrix(key);
		Digraph digraph = new Digraph(text);
		// matrix.printMatrix();
		// digraph.printDigraph();
		HashMap<Character, Integer[]> mainMatrix = matrix.getMainMatrix();
		Character[][] playFairMatrix = matrix.getPlayFairMatrix();
		ArrayList<Character[]> digraphList = digraph.getDigraph(), encriptedDigraph = new ArrayList<Character[]>();
		for (Character[] digraphPair : digraphList) {
			Integer[] first = mainMatrix.get(digraphPair[0]), second = matrix
					.getMainMatrix().get(digraphPair[1]);
			Character[] pair = new Character[2];
			// System.out.println(first + " " + second);
			// is rectangle
			if (first[0] != second[0] && first[1] != second[1]) {
				pair[0] = playFairMatrix[first[0]][second[1]];
				pair[1] = playFairMatrix[second[0]][first[1]];
			}
			// same row
			else if (first[1] == second[1]) {
				pair[0] = playFairMatrix[(first[0] + 1) % 5][first[1]];
				pair[1] = playFairMatrix[(second[0] + 1) % 5][second[1]];
			}
			// same column
			else {
				pair[0] = playFairMatrix[first[0]][(first[1] + 1) % 5];
				pair[1] = playFairMatrix[second[0]][(second[1] + 1) % 5];
			}
			encriptedDigraph.add(pair);
		}
		String ret = "";
		for (Character[] cha : encriptedDigraph) {
			ret += cha[0].toString();
			ret += cha[1].toString();
		}
		return ret;
	}

	public ActionManager getActionManager() {
		return actionManager;
	}

	public String getCodeToBreak() {
		return codeToBreak;
	}

	public void setCodeToBreak(String codeToBreak) {
		this.codeToBreak = codeToBreak;
	}

	public ArrayList<BruteForceAttackResult> getPossibleSolutions() {
		return possibleSolutions;
	}

	public void setPossibleSolutions(
			ArrayList<BruteForceAttackResult> possibleSolutions) {
		this.possibleSolutions = possibleSolutions;
	}

	public String getBestPlainText() {
		return bestPlainText;
	}

	public void setBestPlainText(String bestPlainText) {
		this.bestPlainText = bestPlainText;
	}

	public Character getInterchangeChar2() {
		return interchangeChar2;
	}

	public void setInterchangeChar2(Character interchangeChar2) {
		this.interchangeChar2 = interchangeChar2;
	}

	public Character getInterChar() {
		return interChar;
	}

	public void setInterChar(Character interChar) {
		this.interChar = interChar;
	}

	public Character getInterchangeChar1() {
		return interchangeChar1;
	}

	public void setInterchangeChar1(Character interchangeChar1) {
		this.interchangeChar1 = interchangeChar1;
	}

	public Character getFinalChar() {
		return finalChar;
	}

	public void setFinalChar(Character finalChar) {
		this.finalChar = finalChar;
	}

	public HashMap<String, Double> getTrigrams() {
		return trigrams;
	}

	public void setTrigrams(HashMap<String, Double> trigrams) {
		this.trigrams = trigrams;
	}

}
